import {desDecrypt, genDesKey, rsaPubEncrypt} from "@/utils/EncryptUtils.js";
import useAccountStore from "@/store/modules/AccountStore.js";
import {postFile} from "@/utils/http.js";
import {fileSlice, getFileMD5} from "@/utils/FileUtils.js";
import {resolvePromise_data, resolvePromise_status} from "@/utils/ReturnUtils.js";
import {getDateTimeFormatNow} from "@/utils/DateUtils.js";

// **************
// * 文件上传状态 *
// **************
const FILE_UPLOAD_SUCCESS = -1
const FILE_UPLOAD_FAIL = -2

// 下一个分片索引字段名
export const NEXT_CHUNK_IDX_FIELD_NAME = "nextChunkIdx"
// 文件上传响应给网页元素的提示信息
export const TIP_MSG_FIELD_NAME = 'tipMessage'
// 文件在线访问路径字段名
export const FILE_URL_FIELD_NAME = 'fileUrl'
// 响应给网页元素的文件上传状态字段名
export const STATUS_FIELD_NAME = 'status'

/**
 * 文件上传
 *
 * @param file 原生文件对象
 */
export const fileUploadAPI = async (file) => {
    // 获取当前时间
    const now = getDateTimeFormatNow();
    // 文件 md5
    const fileMD5 = await getFileMD5(file);
    // 文件分片
    const fileSlices = await fileSlice(file);
    // 获取接下来要上传的分片索引
    const res = await getNextFileSliceIdx(file, fileMD5, fileSlices.length)
    const result = genFileUploadResult(res);
    if (result !== null) return result
    // 分片上传
    for (let i = res[NEXT_CHUNK_IDX_FIELD_NAME]; i < fileSlices.length; i++) {
        // 上传分片
        const res = await fileSliceUploadAPI(now, fileMD5, file, fileSlices.length, i, fileSlices[i])
        const result = genFileUploadResult(res);
        if (result !== null) return result
        // 继续分片上传
    }
}

/**
 * 生成响应给网页元素的文件上传结果
 *
 * @param res API 方法响应结果 res
 */
const genFileUploadResult = (res) => {
    // 本次请求失败
    if (!res) {
        return {[STATUS_FIELD_NAME]: false, [TIP_MSG_FIELD_NAME]: '文件上传失败', [FILE_URL_FIELD_NAME]: ''}
    }
    // 文件之前上传过，文件上传成功
    if (res[NEXT_CHUNK_IDX_FIELD_NAME] === FILE_UPLOAD_SUCCESS) {
        return {
            [STATUS_FIELD_NAME]: true,
            [TIP_MSG_FIELD_NAME]: res[TIP_MSG_FIELD_NAME],
            [FILE_URL_FIELD_NAME]: res[FILE_URL_FIELD_NAME]
        }
    }
    // 文件上传失败
    if (res[NEXT_CHUNK_IDX_FIELD_NAME] === FILE_UPLOAD_FAIL) {
        return {
            [STATUS_FIELD_NAME]: false,
            [TIP_MSG_FIELD_NAME]: res[TIP_MSG_FIELD_NAME],
            [FILE_URL_FIELD_NAME]: res[FILE_URL_FIELD_NAME]
        }
    }
    return null
}

/**
 * 获取文件下一个要上传的分片
 *
 * @param file 原生文件对象
 * @param md5 md5
 * @param fileSliceLen 文件分片总个数
 * @returns {Promise<Response>}
 */
export function getNextFileSliceIdx(file, md5, fileSliceLen) {
    // DES 密钥
    const desKey = genDesKey()
    // 文件上传表单数据对象
    const formData = new FormData();
    formData.append('account', rsaPubEncrypt(useAccountStore().account))
    formData.append('filename', rsaPubEncrypt(file.name))
    formData.append('fileType', rsaPubEncrypt(file.type))
    formData.append('md5', rsaPubEncrypt(md5))
    formData.append('fileSliceLen', fileSliceLen)
    formData.append('desKey', rsaPubEncrypt(desKey))
    return postFile('/file/upload/slice/next', formData).then(
        res => {
            res = desDecrypt(res, desKey)
            // 本次请求成功
            if (res.status) {
                return resolvePromise_data(res)
            } else {
                return resolvePromise_status(res)
            }
        }
    )
}

/**
 * 文件分片上传 API
 *
 * @param now 当前时间
 * @param md5 文件 MD5
 * @param file 原生文件对象
 * @param fileSliceLen 文件分片数
 * @param fileSliceIdx 当前要上传的分片索引
 * @param fileSlice 当前要上传的分片
 * @returns {Promise<void>}
 */
export const fileSliceUploadAPI = async (now, md5, file, fileSliceLen, fileSliceIdx, fileSlice) => {
    // DES 密钥
    const desKey = genDesKey()
    // 文件上传表单数据对象
    const formData = new FormData();
    formData.append('account', rsaPubEncrypt(useAccountStore().account))
    formData.append('filename', rsaPubEncrypt(file.name))
    formData.append('fileUploadTime', now)
    formData.append('fileSize', file.size)
    formData.append('fileType', rsaPubEncrypt(file.type))
    formData.append('md5', rsaPubEncrypt(md5))
    formData.append('chunkTotal', fileSliceLen)
    formData.append('chunkIdx', fileSliceIdx)
    formData.append('chunk', fileSlice)
    formData.append('desKey', rsaPubEncrypt(desKey))
    return postFile('/file/upload/slice', formData).then(
        res => {
            res = desDecrypt(res, desKey)
            // 本次请求成功
            if (res.status) {
                return resolvePromise_data(res)
            } else {
                return resolvePromise_status(res)
            }
        }
    )
}